//! Module for game state management
//!
//! This module handles the game state, turn management, move execution,
//! and game result determination.

use std::fmt;
use serde::{Serialize, Deserialize};
use crate::{Position, Side, GameResult, ChessResult, ChessError};
use crate::board::Board;
use crate::piece::{Piece, PieceType};
use crate::moves::{Move, MoveGen};

/// Represents a move in the game history with additional information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MoveRecord {
    pub mv: Move,
    pub piece_moved: Piece,
    pub piece_captured: Option<Piece>,
    pub check: bool,
}

/// Represents the state of a chess game
#[derive(Clone, Serialize, Deserialize)]
pub struct Game {
    /// The game board
    pub board: Board,
    /// The current side to move
    pub current_side: Side,
    /// The game result
    pub result: GameResult,
    /// The move history
    pub history: Vec<MoveRecord>,
    /// Number of moves without capture (for draw detection)
    pub moves_without_capture: u32,
    /// Maximum number of moves without capture before draw
    pub draw_threshold: u32,
}

impl Game {
    /// Create a new game with the initial board setup
    pub fn new() -> Self {
        Game {
            board: Board::new_with_pieces(),
            current_side: Side::Red,
            result: GameResult::Ongoing,
            history: Vec::new(),
            moves_without_capture: 0,
            draw_threshold: 60, // 30 moves for each side
        }
    }

    /// Create a new game with a custom board
    pub fn with_board(board: Board) -> Self {
        Game {
            board,
            current_side: Side::Red,
            result: GameResult::Ongoing,
            history: Vec::new(),
            moves_without_capture: 0,
            draw_threshold: 60,
        }
    }

    /// Make a move on the board
    pub fn make_move(&mut self, mv: Move) -> ChessResult<()> {
        // Check if the game is already over
        if self.result != GameResult::Ongoing {
            return Err(ChessError::GameOver);
        }

        // Check if the piece belongs to the current side
        let piece = self.board.get_piece(mv.from)
            .ok_or(ChessError::InvalidMove)?;
        
        if piece.side != self.current_side {
            return Err(ChessError::WrongTurn);
        }

        // Validate the move
        MoveGen::validate_move(&self.board, mv)?;

        // Check if the move would leave the player in check
        if self.would_be_in_check(mv) {
            return Err(ChessError::IllegalMove("Move would leave or put king in check"));
        }

        // Record the move
        let captured = self.board.get_piece(mv.to);
        
        // Execute the move
        self.board.move_piece(mv.from, mv.to)?;

        // Update moves without capture counter
        if captured.is_some() {
            self.moves_without_capture = 0;
        } else {
            self.moves_without_capture += 1;
        }

        // Check if the opponent is in check or checkmate
        let is_check = self.is_in_check(self.current_side.opposite());
        let has_legal_moves = self.has_legal_moves(self.current_side.opposite());
        
        // Record the move in history
        self.history.push(MoveRecord {
            mv,
            piece_moved: piece,
            piece_captured: captured,
            check: is_check,
        });

        // Update game result
        if is_check && !has_legal_moves {
            // Checkmate
            self.result = GameResult::Win(self.current_side);
        } else if !has_legal_moves {
            // Stalemate
            self.result = GameResult::Draw;
        } else if self.moves_without_capture >= self.draw_threshold {
            // Draw by move limit
            self.result = GameResult::Draw;
        }

        // Switch sides
        self.current_side = self.current_side.opposite();

        Ok(())
    }

    /// Check if the given side is in check
    pub fn is_in_check(&self, side: Side) -> bool {
        // Find the general
        let general_pos = match self.board.find_piece(PieceType::General, side) {
            Some(pos) => pos,
            None => return false, // No general, can't be in check (shouldn't happen in a real game)
        };

        // Check if any opponent piece can capture the general
        for (pos, piece) in self.board.get_pieces(side.opposite()) {
            let mv = Move::new(pos, general_pos);
            if MoveGen::validate_move(&self.board, mv).is_ok() {
                return true;
            }
        }

        // Special case: "flying general" rule
        // Check if the two generals are facing each other with no pieces between
        let opponent_general_pos = match self.board.find_piece(PieceType::General, side.opposite()) {
            Some(pos) => pos,
            None => return false,
        };

        if general_pos.file == opponent_general_pos.file {
            let mut has_piece_between = false;
            let min_rank = general_pos.rank.min(opponent_general_pos.rank);
            let max_rank = general_pos.rank.max(opponent_general_pos.rank);
            
            for rank in (min_rank + 1)..max_rank {
                if !self.board.is_empty(Position { file: general_pos.file, rank }) {
                    has_piece_between = true;
                    break;
                }
            }
            
            if !has_piece_between {
                return true;
            }
        }

        false
    }

    /// Check if the given side has any legal moves
    pub fn has_legal_moves(&self, side: Side) -> bool {
        for (pos, _) in self.board.get_pieces(side) {
            let moves = MoveGen::generate_piece_moves(&self.board, pos);
            for mv in moves {
                if !self.would_be_in_check(mv) {
                    return true;
                }
            }
        }
        false
    }

    /// Check if making a move would leave the current side in check
    fn would_be_in_check(&self, mv: Move) -> bool {
        let mut game_copy = self.clone();
        let piece = game_copy.board.get_piece(mv.from).unwrap();
        let side = piece.side;
        
        // Execute the move on the copy
        game_copy.board.move_piece(mv.from, mv.to).unwrap();
        
        // Check if the side is in check after the move
        game_copy.is_in_check(side)
    }

    /// Get all legal moves for the current side
    pub fn get_legal_moves(&self) -> Vec<Move> {
        let all_moves = MoveGen::generate_moves(&self.board, self.current_side);
        all_moves.into_iter()
            .filter(|&mv| !self.would_be_in_check(mv))
            .collect()
    }

    /// Get the last move made in the game
    pub fn last_move(&self) -> Option<&MoveRecord> {
        self.history.last()
    }

    /// Undo the last move
    pub fn undo_move(&mut self) -> ChessResult<()> {
        let last_move = self.history.pop()
            .ok_or(ChessError::InvalidMove)?;
        
        // Remove the piece from the destination
        self.board.remove_piece(last_move.mv.to);
        
        // Restore the captured piece if any
        if let Some(captured) = last_move.piece_captured {
            self.board.place_piece(last_move.mv.to, captured);
        }
        
        // Move the piece back
        self.board.place_piece(last_move.mv.from, last_move.piece_moved);
        
        
        // Switch back to the previous side
        self.current_side = self.current_side.opposite();
        
        // Reset game result to ongoing
        self.result = GameResult::Ongoing;
        
        // Update moves without capture counter
        if last_move.piece_captured.is_some() {
            // We're undoing a capture, so we need to recalculate
            // This is a simplification; a real implementation would store the previous value
            self.moves_without_capture = 0;
        } else if self.moves_without_capture > 0 {
            self.moves_without_capture -= 1;
        }
        
        Ok(())
    }

    /// Convert the game state to FEN-like notation for AI training
    pub fn to_fen(&self) -> String {
        let mut fen = self.board.to_fen();
        
        // Add side to move
        fen.push(' ');
        fen.push(match self.current_side {
            Side::Red => 'r',
            Side::Black => 'b',
        });
        
        // Add move number
        fen.push(' ');
        fen.push_str(&(self.history.len() / 2 + 1).to_string());
        
        fen
    }

    /// Get a numerical representation of the board state for AI training
    pub fn get_state_features(&self) -> Vec<Vec<Vec<i8>>> {
        // Create a 9x10x14 tensor representation
        // 7 piece types * 2 sides = 14 channels
        let mut features = vec![vec![vec![0i8; 14]; 9]; 10];
        
        for rank in 0..10 {
            for file in 0..9 {
                let pos = Position { file: file as i8, rank: rank as i8 };
                if let Some(piece) = self.board.get_piece(pos) {
                    let channel = match (piece.side, piece.piece_type) {
                        (Side::Red, PieceType::General) => 0,
                        (Side::Red, PieceType::Advisor) => 1,
                        (Side::Red, PieceType::Elephant) => 2,
                        (Side::Red, PieceType::Horse) => 3,
                        (Side::Red, PieceType::Chariot) => 4,
                        (Side::Red, PieceType::Cannon) => 5,
                        (Side::Red, PieceType::Soldier) => 6,
                        (Side::Black, PieceType::General) => 7,
                        (Side::Black, PieceType::Advisor) => 8,
                        (Side::Black, PieceType::Elephant) => 9,
                        (Side::Black, PieceType::Horse) => 10,
                        (Side::Black, PieceType::Chariot) => 11,
                        (Side::Black, PieceType::Cannon) => 12,
                        (Side::Black, PieceType::Soldier) => 13,
                    };
                    
                    features[rank][file][channel] = 1;
                }
            }
        }
        
        features
    }
}

impl fmt::Display for Game {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{}", self.board)?;
        writeln!(f, "Current turn: {:?}", self.current_side)?;
        writeln!(f, "Game status: {:?}", self.result)?;
        
        if let Some(last_move) = self.last_move() {
            write!(f, "Last move: ")?;
            let from = last_move.mv.from;
            let to = last_move.mv.to;
            writeln!(f, "({},{}) -> ({},{})", from.file, from.rank, to.file, to.rank)?;
        }
        
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_new_game() {
        let game = Game::new();
        assert_eq!(game.current_side, Side::Red);
        assert_eq!(game.result, GameResult::Ongoing);
        assert!(game.history.is_empty());
    }

    #[test]
    fn test_make_move() {
        let mut game = Game::new();
        
        // Move a red soldier forward
        let mv = Move::new(
            Position { file: 4, rank: 3 },
            Position { file: 4, rank: 4 }
        );
        
        assert!(game.make_move(mv).is_ok());
        assert_eq!(game.current_side, Side::Black);
        assert_eq!(game.history.len(), 1);
    }

    #[test]
    fn test_invalid_move() {
        let mut game = Game::new();
        
        // Try to move a piece that doesn't exist
        let mv = Move::new(
            Position { file: 4, rank: 4 },
            Position { file: 4, rank: 5 }
        );
        
        assert!(game.make_move(mv).is_err());
    }

    #[test]
    fn test_wrong_turn() {
        let mut game = Game::new();
        
        // Try to move a black piece on red's turn
        let mv = Move::new(
            Position { file: 4, rank: 6 },
            Position { file: 4, rank: 5 }
        );
        
        assert!(game.make_move(mv).is_err());
    }

    #[test]
    fn test_check_detection() {
        let mut board = Board::new();
        
        // Place kings
        board.place_piece(
            Position { file: 4, rank: 0 },
            Piece::new(PieceType::General, Side::Red)
        );
        board.place_piece(
            Position { file: 4, rank: 9 },
            Piece::new(PieceType::General, Side::Black)
        );
        
        // Place a black chariot to check the red king
        board.place_piece(
            Position { file: 4, rank: 1 },
            Piece::new(PieceType::Chariot, Side::Black)
        );
        
        let game = Game::with_board(board);
        assert!(game.is_in_check(Side::Red));
        assert!(!game.is_in_check(Side::Black));
    }
}